import AVFoundation
import CoreData
import UIKit

// 游戏回调
protocol GameCallback: NSObjectProtocol {
    func onDifficultyUp(_ difficulty: Int)  // 难度增加事件
    func onGameOver(_ isWon: Bool)          // 游戏结束事件
    func onScore(_ score: Int)              // 得分事件
}

// 游戏
class Game {
    // 状态
    public enum Status {
        case END        // 已结束
        case PAUSED     // 暂停中
        case PLAYING    // 游戏中
    }

    private var mApple: SnakeBody!              // 果子
    private let mCallback: GameCallback         // 回调
    private var mDifficulty: Int                // 难度
    private let mDrawer: CGContext              // 绘图者
    private var mEnableMusic: Bool              // 音乐开关
    private let mGameBoard: UIView              // 游戏区域
    private var mMusicPlayer: AVAudioPlayer!    // 音乐播放者
    private var mScore: Int                     // 得分
    private var mSnake: Snake!                  // 蛇
    private var mStatus: Status                 // 状态
    private var mTimer: Timer!                  // 定时器

    /**
     * 构造方法。
     * @param callback  回调
     * @param drawer    绘图者
     * @param gameBoard 游戏区域
     */
    public init(_ callback: GameCallback, drawer: CGContext, gameBoard: UIView) {
        // 初始化成员
        mCallback = callback
        mDifficulty = 1
        mDrawer = drawer
        mEnableMusic = false
        mGameBoard = gameBoard
        let path = Bundle.main.path(forResource: Const.MUSIC_FILE_NAME, ofType: Const.MUSIC_EXTEND)!
        let url = URL(fileURLWithPath: path)
        do {
            mMusicPlayer = try AVAudioPlayer(contentsOf: url)
            mMusicPlayer.numberOfLoops = -1    // 设置为循环播放
        } catch {
            print(error.localizedDescription)
        }
        mScore = 0
        mStatus = .END

        // 重绘游戏区域
        self.drawGameBoard()
        mGameBoard.setNeedsDisplay()
    }
    
    /**
     * 在随机位置创建果子。
     */
    private func createApple() {
        var point = CGPoint()
        var used = false

        // 创建果子
        repeat {
            // 取 x、y 的随机值
            point.x = CGFloat(arc4random_uniform(UInt32(Const.GAME_BOARD_GRID_COUNT_H - 2)) + 1)
            point.y = CGFloat(arc4random_uniform(UInt32(Const.GAME_BOARD_GRID_COUNT_V - 2)) + 1)
            used = self.pointIsUsed(point)
        } while used
        mApple = SnakeBody(mDrawer, point: point, type: .APPLE)
    }

    /**
     * 画游戏区域。
     */
    private func drawGameBoard() {
        mDrawer.setFillColor(Const.GAME_BOARD_COLOR)
        mDrawer.fill(CGRect(x: 0, y: 0, width: mGameBoard.frame.width, height: mGameBoard.frame.height))
    }

    /**
     * 游戏结束。
     * @param isWon 胜利则为 true，否则为 false
     */
    private func gameOver(_ isWon: Bool) {
        // 将状态设置为已结束
        mStatus = .END
        
        // 停止定时器
        mTimer.invalidate()
        
        // 停止播放音乐
        if mEnableMusic {
            mMusicPlayer.stop()
        }

        // 发送通知
        mCallback.onGameOver(isWon)
    }
    
    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private func isWon() -> Bool {
        return mScore >= Const.WIN_NUMBER
    }
    
    /**
     * 滑动手势的响应方法。
     * @param direction 方向
     */
    public func onFling(_ direction: UISwipeGestureRecognizer.Direction) {
        if mStatus != .PLAYING {
            return
        }
        mSnake.turn(direction)
    }

    /**
     * 定时器事件的响应方法。
     */
    @objc public func onTimer() {
        // 移动蛇
        let isDead = mSnake.move()
        if isDead {
            self.gameOver(false)
            return
        }

        // 检查是否碰到果子
        if mSnake.head.position == mApple.position {
            // 碰到果子，将果子加入蛇链表
            mSnake.eat(mApple)
            self.createApple()
            mScore += 1
            mCallback.onScore(mScore)
            
            // 判断是否胜利
            if self.isWon() {
                self.gameOver(true)
                return
            }
            
            // 判断是否达到增加难度的条件
            if mScore % Const.DIFF_BASE == 0 && mDifficulty != Const.DIFF_MAX {
                mDifficulty += 1
                mCallback.onDifficultyUp(mDifficulty)
                mTimer.invalidate()
                let elapse = Const.TIMER_INIT - Const.TIMER_GROW * TimeInterval(mDifficulty - 1)
                mTimer = Timer.scheduledTimer(timeInterval: elapse, target: self, selector: #selector(Game.onTimer), userInfo: nil, repeats: true)
            }
        }
        
        // 通知游戏区域重绘
        mGameBoard.setNeedsDisplay()
    }

    /**
     * 游戏暂停。
     */
    public func pause() {
        if mStatus == .PLAYING {
            // 停止定时器
            mTimer.invalidate()
            
            // 停止播放音乐
            if mEnableMusic {
                mMusicPlayer.pause()
            }

            // 将状态设置为已暂停
            mStatus = .PAUSED
        } else if mStatus == .PAUSED {
            // 开启定时器
            let elapse = Const.TIMER_INIT - Const.TIMER_GROW * Double(mDifficulty - 1)
            mTimer = Timer.scheduledTimer(timeInterval: elapse, target: self, selector: #selector(Game.onTimer), userInfo: nil, repeats: true)
            
            // 播放音乐
            if mEnableMusic {
                mMusicPlayer.play()
            }
    
            // 将状态设置为游戏中
            mStatus = .PLAYING
        }
    }

    /**
     * 播放音乐。
     * @return 正在播放音乐则为 true，否则为 false
     */
    public func playMusic() -> Bool {
        mEnableMusic = !mEnableMusic
        
        if mEnableMusic {
            if mStatus == .PLAYING {
                mMusicPlayer.play()
            }
        } else {
            mMusicPlayer.pause()
        }
        
        return mEnableMusic
    }

    /**
     * 判断某个点是否被占用。
     * @param point 点
     * @return 被占用了则返回 true，否则返回 false
     */
    private func pointIsUsed(_ point: CGPoint) -> Bool {
        // 检查点是否在蛇上
        if mSnake != nil {
            if mSnake.pointIsOnMe(point) {
                return true
            }
        }

        // 检查点是否在果子上
        if mApple != nil {
            return mApple.position == point
        }

        return false
    }

    /**
     * 游戏开始。
     */
    public func start() {
        // 重绘游戏区域
        self.drawGameBoard()

        // 创建蛇
        mSnake = Snake(mDrawer)
        
        // 创建果子
        self.createApple()

        // 通知游戏区域重绘
        mGameBoard.setNeedsDisplay()

        // 重置难度
        mDifficulty = 1

        // 重置分数
        mScore = 0

        // 开启定时器
        if mTimer != nil {
            mTimer.invalidate()
        }
        mTimer = Timer.scheduledTimer(timeInterval: Const.TIMER_INIT, target: self, selector: #selector(Game.onTimer), userInfo: nil, repeats: true)

        // 播放音乐
        if mEnableMusic {
            mMusicPlayer.play()
        }

        // 将状态设置为游戏中
        mStatus = .PLAYING
    }
    
    /**
     * 获取游戏状态。
     */
    public var status: Status {
        get {
            return mStatus
        }
    }
}
